home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C++ / Snippets / Scan Folder 1.3 / File Scanner Source / Scan.c++ < prev    next >
Encoding:
Text File  |  1994-04-07  |  19.0 KB  |  521 lines  |  [TEXT/KAHL]

  1. // FILE    : Scan.c++
  2. // NAME    : Scan Folder Utilities
  3. // VERSION : 1.3
  4. // DATE    : Dec. 29, 1993
  5. // UPDATE  : Feb. 21, 1994
  6. // AUTHOR  : Hiep Dam, 3G Software
  7. // CONTACT : Via America Online at StarLabs, or
  8. //             2226 Parkside Ave. #302, Los Angeles, CA 90031
  9. // STATUS  : This code is freeware and is in the public domain. Feel free to use it
  10. //             if you find it useful (it would be nice if you gave me some credit,
  11. //             though  ;)
  12.  
  13. // P.S.
  14. //    READ THE HEADER!!! (Scan.h)
  15.  
  16. // ----------------------------------------------------------------
  17.  
  18. // NOTES   :
  19. //        So what are these routines, anyway? Well, I'm developing a game right
  20. //    now, and I'm trying to make it as extensible as possible. That's why
  21. //    I'm implementing a "Plug-Ins" type of scheme: within the same folder as
  22. //    the application, I plan to have a folder named "Plug-Ins" or "Data"
  23. //    or what-have-you, with the requisite plug-in files inside. This scheme is
  24. //    very much like Photoshop's Plug-Ins folder, Firefall's Data folder, etc.
  25. //    "Enough!" you say. OK. Anyway, this is all fine and dandy, but how does
  26. //    one *actually* implement something like this??? I consider myself an
  27. //    intermediate programmer (when I can write a pixel-blitting routine by myself
  28. //    or understand how the hell to use cluts and pltts, then I'll consider
  29. //    myself an advanced programmer) so programming something like this is a
  30. //    tad tricky, especially if you're dealing with the File Manager—too many
  31. //    ioThisPtrs, vRefThats, and (Funky)TypeCasting->ioCrazy.thisFileDirIDs!!!!
  32.  
  33. //        OK, down to the code. Luckily I have Think Reference, and I was able
  34. //    to find some information on searching a volume. By the way, if you don't
  35. //    have Think Reference, I suggest you stop reading right now, get dressed,
  36. //    get some money, and go to the local computer store and get your copy
  37. //    right now. Right now, I say! (or call MacConnection)...
  38.  
  39. //        Think Reference had an example routine on searching all the files in a
  40. //    volume (File Manager, "Searching All Files On A Volume"). I basically
  41. //    copied off of that, but made some modifications here and there where it
  42. //    was necessary. I had to dig up information on HFileInfo's, volume id's,
  43. //    directory id's, "hard" numbers, ioNamePtrs, and on and on and on...
  44. //    Too many reference numbers! Sigh. Oh well. Thank goodness I don't design
  45. //    scsi hard disks. Read the accompanying "HFS Illuminated" file for a layman's
  46. //    explanation of what all those numbers mean...
  47.  
  48. //        Boy, am I getting off track! Down to business: remembering my goal
  49. //    mentioned earlier, my initial scanning code scanned *everything* within
  50. //    the same folder as the application. This is not that good. It should scan
  51. //    only in the specified folder (i.e. "Plug-Ins"), and not in any other folders.
  52. //    We might not want to use a plug-in; to do that we should be able to place
  53. //    the plug-in *anywhere* except the Plug-Ins folder. So I changed the scanning
  54. //    code to only start scanning in the folder passed to it. This is much better!
  55. //    Note that if you still want to start scanning in the same folder as the 
  56. //    application, just pass nil in the folderName argument.
  57.  
  58. //        After that, I added specific search criteria: scan folder by file type,
  59. //    by file creator, by both type and creator, or by file name. Or you can just
  60. //    scan everything in the folder. This code hasn't been really extensively tested,
  61. //    but it should work. It definitely works on a Mac IIsi w/ 5 megs of ram running
  62. //    System 7.1... It was written in Symantec C++ 6.0, but *should* work on THINK C
  63. //    5.0 (just change .c++ to .c).
  64.  
  65. //        Some final notes: Macintosh Tech Note #68 (Searching Volumes) states that
  66. //    that you should search for files via _PBCatSearch, if it's available. It's
  67. //    much faster than the recursive _PBGetCatInfo method I use below, but to be
  68. //    honest, I think the method below (albeit a tad messy and recursive) is way
  69. //    easier to understand than _PBCatSearch. I've read the section on it in Inside
  70. //    Mac VI many times, but I still don't know how to fill in the necessary fields
  71. //    for PBCatSearch. Is it me or is that book way too technical (and not enough
  72. //    clear and "meaty" examples, to boot!) Anyway, the intended use for the Scan
  73. //    functions is just to scan for a moderate amount of files located in a folder
  74. //    within the same directory as the application running it, not as a file
  75. //    searcher or cataloguer, so they should work just fine as they were intended.
  76.  
  77. //        I hope this code helps you in your programming efforts or further your
  78. //    understanding of Macintosh programming. I can't say how much I learned just
  79. //    from downloading source code from other programmers. This is the best way of
  80. //    learning Mac programming, if you ask me (but only if the source code is
  81. //    sufficiently commented, else it's a disaster!) This code is free, and you
  82. //    can use it any way you want. Any comments, suggestions, queries, death-threats,
  83. //    bug reports, etc. can be made to me at America Online, StarLabs. Enjoy!!!
  84.  
  85. //    Hiep Dam
  86. //    3G Software
  87.  
  88.  
  89. //    Version History:
  90. //    1.0: Worked fine with my demo application...
  91. //    1.1: Fixed a bug when passing nil as folderName. It seems gCPB.ioNamePtr wasn't
  92. //         set when passing a nil folderName; added code in InitScan to fix that.
  93. //  1.2: Fixed a bug in GetFolderDirID. Same bug as above!! I forgot to set
  94. //         gCPB.ioNamePtr and gCPB.ioVRefNum. The routine worked fine if called by
  95. //         Scan routines (since they set ioNamePtr & ioVRefNum) but calling GetFolderDirID
  96. //         directly caused it to return -1, since it can't find any folder with an
  97. //         empty name. Oops!! (1/5/94)
  98. //    1.3: Added support for aliases. Now you can tell the scanning routines to
  99. //         resolve the aliases for you. You can also tell the routines to resolve folder
  100. //         aliases as well. Whew!! Also fixed and changed the code here and
  101. //         there. You know, the little stuff. (2/20/94)
  102.  
  103. // ----------------------------------------------------------------
  104.  
  105. #include <Packages.h>    // Needed to use _IUEqualString
  106. #include <Aliases.h>    // Needed for _ResolveAliasFile
  107. #include "Scan.h"
  108.  
  109. // ----------------------------------------------------------------
  110.  
  111. // Some globals
  112.  
  113. // ** Version 1.3 Change **: Made globals below static...
  114.  
  115. // Used by ScanContents and ScanDirectory4Folder only...
  116. static HFileInfo gCPB;                // Low-level volume information used by _PBGetCatInfo
  117. static Str255 gCurFileName;            // Name of current file/folder we're looking at
  118. static long gDirScratch = -1;        // Used to temporarily  hold a directory id
  119.  
  120. // Used by all of the routines...
  121. static FSSpec *gCurFileArray = nil;    // Array to hold list of FSSpec's
  122. static short gCurFileIndex;            // Current index into above-mentioned array
  123. static short gMaxFileLimit;            // Size of above-mentioned array
  124. static Boolean gRestrictByType;        // Look for file based on file type?
  125. static Boolean gRestrictByCreator;    // Look for file based on file creator?
  126. static Boolean gRestrictByName;        // Look for file based on name?
  127. static Boolean gResolveAlias;        // Do we resolve aliases?    Vers 1.3 addition
  128. static Boolean gResolveFolderAlias;    // Do we resolve folder aliases & look in there? Vers 1.3 addition
  129. static OSType gFileType;            // File type, i.e. "TEXT", "PICT", etc.
  130. static OSType gCreatorType;            // File creator, i.e. "SPNT", "KAHL", etc.
  131. static Str63 gFileName;                // File name.
  132. static FSSpec gTempSpec;            // Used to temporarily make FSSpec's. Vers 1.3 addition
  133.  
  134. // ----------------------------------------------------------------
  135.  
  136. // Pascal string functions below courtesy of Alex. D. Chaffee,
  137. // from his CreatePath.c source code...
  138.  
  139. // ** Version 1.3 Change **: Change name of Length to PLength to avoid clash
  140. // with THINK C 6.0 syntax...
  141.  
  142. //    return length of string
  143. #define PLength(s) (int)(*(s))
  144.  
  145. // copy part of one pascal string to another
  146. void Copy(unsigned char *dest, unsigned char *src, int start, int end)
  147. {
  148.     int length = end-start+1;
  149.     if (length > 0) {
  150.         *dest = length;
  151.         BlockMove(src+start, dest+1, length);
  152.     } else
  153.         *dest = 0;
  154. }
  155.  
  156. //    concatenate a CHAR onto a string
  157. void Concat(unsigned char *dest, unsigned char *src, char c)
  158. {
  159.     Copy(dest, src, 1, PLength(src));
  160.     dest[0]++;
  161.     dest[dest[0]]=c;
  162. }
  163.  
  164. // Prefix a CHAR onto a string; this is my own routine, modeled
  165. // after Concat
  166. void Precat(unsigned char *dest, unsigned char *src, char c) {
  167.     Copy(dest, src, 1, PLength(src));
  168.     // Shift everything down
  169.     short strEnd = PLength(src);
  170.     if (strEnd == 63)
  171.         strEnd--;
  172.     for (short i = PLength(src)+1; i > 0; i--)
  173.         dest[i] = dest[i-1];
  174.     if (dest[0] < 63)
  175.         dest[0]++; // Update Pascal string length byte
  176.     dest[1] = c;
  177. }
  178.  
  179. // ----------------------------------------------------------------
  180.  
  181. // ScanContents.
  182. // Scans starting at dirIDToSearch, based on parameters set in the globals
  183. // above. Admittedly a messy and lazy implementation, but it's recursive and
  184. // I didn't want to pass identical parameters to it every time.
  185. // This is the "core" of all the scanning routines.
  186.  
  187. void ScanContents(long dirIDToSearch)
  188. {
  189.     short index = 1;
  190.     OSErr err;
  191.     Boolean matchType = false;
  192.     Boolean matchCreator = false;
  193.     Boolean matchName = false;
  194.     Boolean addFSSpec = false;
  195.     Boolean wasFolder, wasAlias;
  196.     
  197.     do {
  198.         gCPB.ioFDirIndex = index;    // set up index
  199.         // do this every time since PBGetCatInfo returns ioFlNum
  200.         // in this field
  201.         gCPB.ioDirID = dirIDToSearch;
  202.         err = PBGetCatInfo((CInfoPBRec*)&gCPB, false);
  203.         if(err == noErr) {
  204.             // check to see if the file is a folder
  205.             if ((gCPB.ioFlAttrib & ioDirMask) != 0) {    // Version 1.3 Change: made more elegant
  206.                 // found a directory, so search on.
  207.                 // Make recursive call
  208.                 ScanContents(gCPB.ioDirID);
  209.                 err = 0;
  210.             }
  211.             else if ((gCPB.ioFlFndrInfo.fdType == 'fdrp') && gResolveFolderAlias) {
  212.                 // 'fdrp' == kContainerFolderAliasType
  213.                 // First, make a FSSpec of the alias file
  214.                 (void)FSMakeFSSpec(gCPB.ioVRefNum, gCPB.ioFlParID, gCurFileName, &gTempSpec);
  215.  
  216.                 // Now turn that FSSpec of an alias file (folder in our case)
  217.                 // into the actual file (resolve the reference)
  218.                 (void)ResolveAliasFile(&gTempSpec, true, &wasFolder, &wasAlias);
  219.  
  220.                 gCPB.ioCompletion = nil;
  221.                 BlockMove((Ptr)&gTempSpec.name[0], (Ptr)&gCurFileName[0], gTempSpec.name[0]);
  222.                 gCPB.ioVRefNum = gTempSpec.vRefNum;
  223.                 ((CInfoPBRec)gCPB).dirInfo.ioDrDirID = gTempSpec.parID;
  224.                 gCPB.ioFDirIndex = 0;    // Use name and dirID
  225.                 (void)PBGetCatInfo((CInfoPBRec*)&gCPB, false);
  226.  
  227.                 ScanContents(((CInfoPBRec)gCPB).dirInfo.ioDrDirID);
  228.                 err = 0;
  229.             }
  230.             else {
  231.                 // Found a file, now check to see if it matches
  232.                 // the search criteria...
  233.                 addFSSpec = false;    // Don't add file to FSSpec array — yet.
  234.  
  235.                 if (gRestrictByName) {
  236.                     // Ok, search criteria is by name, so see if our
  237.                     // file's name matches:
  238.                     if (IUEqualString(gCPB.ioNamePtr, gFileName) == 0)
  239.                         matchName = true;    // Yeah, matched!
  240.                     else
  241.                         matchName = false;    // Nope...
  242.                 }
  243.                 if (gRestrictByType) {
  244.                     // Ok, one of the search criteria is by type,
  245.                     // so check the file's type to see if it's what
  246.                     // we're looking for:
  247.                     if (gCPB.ioFlFndrInfo.fdType == gFileType)
  248.                         matchType = true;
  249.                     else
  250.                         matchType = false;
  251.                 }
  252.                 if (gRestrictByCreator) {
  253.                     // Same as above, but look at the file's creator type...
  254.                     if (gCPB.ioFlFndrInfo.fdCreator == gCreatorType)
  255.                         matchCreator = true;
  256.                     else
  257.                         matchCreator = false;
  258.                 }
  259.  
  260.                 // Now we have to determine if we add the file to the
  261.                 // FSSpec array. There's several different combinations
  262.                 // of type/creator search criteria, so we'll have to
  263.                 // do a if-then-else run-through:
  264.                 if (gRestrictByName) {
  265.                     if (matchName)
  266.                         addFSSpec = true;
  267.                 }
  268.                 else if (gRestrictByType && gRestrictByCreator) {
  269.                     if (matchType && matchCreator)
  270.                         // Add file to array only if BOTH type and creator match...
  271.                         addFSSpec = true;
  272.                 }
  273.                 else if (gRestrictByType && !gRestrictByCreator) {
  274.                     if (matchType)
  275.                         addFSSpec = true;
  276.                 }
  277.                 else if (!gRestrictByType && gRestrictByCreator) {
  278.                     if (matchCreator)
  279.                         addFSSpec = true;
  280.                 }
  281.                 else if (!gRestrictByType && !gRestrictByCreator)
  282.                     // This means we'll add the file, no matter it's
  283.                     // type, creator, or name...
  284.                     addFSSpec = true;
  285.  
  286.                 // Is it time to add to the array yet?
  287.                 if (addFSSpec) {
  288.                     // Alright, add the file by making an FSSpec...
  289.                     short fsErr = FSMakeFSSpec(gCPB.ioVRefNum, gCPB.ioFlParID,
  290.                                   gCurFileName, &gCurFileArray[gCurFileIndex++]);
  291.  
  292.                     if (gResolveAlias)
  293.                         (void)ResolveAliasFile(&gCurFileArray[gCurFileIndex - 1], true,
  294.                                 &wasFolder, &wasAlias);
  295.  
  296.                     // Let's do some bounds checking: it's possible there are
  297.                     // more files than there are FSSpec's in the array, so:
  298.                     if (gCurFileIndex == gMaxFileLimit)
  299.                         err = 1; // stop scanning, reached max file limit...
  300.                     
  301.                     // Searching by name, instead of by type or creator, is
  302.                     // slightly different in that once we find the matching file,
  303.                     // we exit the search. Scanning by type or creator, however,
  304.                     // continues until there are no more files or until the
  305.                     // FSSpec array is exhausted.
  306.                     if (gRestrictByName)
  307.                         err = 1;
  308.                 }
  309.             }
  310.             index++;
  311.         }
  312.     } while(err == noErr);
  313. } // END ScanContents
  314.  
  315. // ----------------------------------------------------------------
  316.  
  317. // ScanDirectory4Folder.
  318. // Scans a directory (read=hard disk, etc.) for the specified folder, starting
  319. // at dirIDToSearch. Returns the directory id of the folder in gDirScratch,
  320. // if folder found. You won't have any need to use this function, though.
  321. // The code is pretty much duplicated from ScanContents(). It's simpler, so
  322. // you can use this to write your own scanning routines.
  323.  
  324. void ScanDirectory4Folder(Str63 folderName, long dirIDToSearch) {
  325.     short index = 1;
  326.     OSErr err;
  327.  
  328.     do {
  329.         gCPB.ioFDirIndex = index;
  330.         gCPB.ioDirID = dirIDToSearch;
  331.         err = PBGetCatInfo((CInfoPBRec*)&gCPB, false);
  332.         if(err == noErr) {
  333.             // check to see if the file is a folder
  334.             if ((gCPB.ioFlAttrib & ioDirMask) != 0) {
  335.                 // found a directory
  336.                 if (IUEqualString(gCPB.ioNamePtr, folderName) == 0) {
  337.                     gDirScratch = gCPB.ioDirID;
  338.                     err = 1; // stop scanning
  339.                 }
  340.                 else {
  341.                     ScanDirectory4Folder(folderName, gCPB.ioDirID);
  342.                     err = 0;
  343.                 }
  344.             }
  345.             else {
  346.                 // found a file. Do nothing.
  347.             }
  348.             index++;
  349.         }
  350.     } while(err == noErr);
  351. }    // END ScanDirectory4Folder
  352.  
  353.  
  354. // GetFolderDirID.
  355. // A much more useful function. Returns the directory id
  356. // of the specified folder (you'll have to know the name of the
  357. // folder beforehand, thus users should not change the folder's
  358. // name). Makes a call to ScanDirectory4Folder.
  359. // Returns -1 if folder not found.
  360.  
  361. long GetFolderDirID(Str63 folderName, long startDirID) {
  362.     gDirScratch = -1;    // Initialize global variable
  363.  
  364.     gCPB.ioNamePtr = gCurFileName;    // Version 1.2 Fix
  365.     gCPB.ioVRefNum = 0;                // Ditto...
  366.  
  367.     ScanDirectory4Folder(folderName, startDirID);
  368.     return gDirScratch;
  369. } // END GetFolderDirID
  370.  
  371. // ----------------------------------------------------------------
  372.  
  373. // InitScan.
  374. // Initializes some global variables...
  375.  
  376. void InitScan(FSSpec *fileArray, short arraySize) {
  377.     gCurFileArray = fileArray;    // Array of FSSpec's passed to us
  378.     gCurFileIndex = 0;            // Index into the array; start at 0
  379.     gMaxFileLimit = arraySize;    // Size of array (so we don't overstep bounds)
  380.     gCPB.ioNamePtr = gCurFileName;
  381.     gCPB.ioVRefNum = 0;
  382. } // END InitScan
  383.  
  384. // ----------------------------------------------------------------
  385.  
  386. // ScanFolder.
  387. // See header file for comments on using this function.
  388. // Returns number of files found and placed in the FSSpec array, 0 otherwise.
  389.  
  390. short ScanFolder(Str63 folderName, long startDirID, FSSpec *fileArray, short arraySize,
  391.         Boolean resolveAlias, Boolean resolveFolderAlias) {
  392.     InitScan(fileArray, arraySize);
  393.     // Scan *everything*, so turn off all search criteria...
  394.     gRestrictByType = false;
  395.     gRestrictByCreator = false;
  396.     gRestrictByName = false;
  397.     gResolveAlias = resolveAlias;
  398.     gResolveFolderAlias = resolveFolderAlias;
  399.  
  400.     long startDir;
  401.  
  402.     if (folderName != nil)
  403.         startDir = GetFolderDirID(folderName, startDirID);
  404.     else
  405.         startDir = startDirID;
  406.     ScanContents(startDir);
  407.     if (gCurFileIndex > 0)
  408.         return gCurFileIndex;
  409.     else
  410.         return 0;
  411. } // END ScanFolder
  412.  
  413. // ----------------------------------------------------------------
  414.  
  415. short ScanFolderByType(Str63 folderName, long startDirID, OSType restriction, FSSpec *fileArray,
  416.         short arraySize, Boolean resolveAlias, Boolean resolveFolderAlias) {
  417.     InitScan(fileArray, arraySize);
  418.     gFileType = restriction;    // OSType to filter through
  419.     gRestrictByType = true;
  420.     gRestrictByCreator = false;
  421.     gRestrictByName = false;
  422.     gResolveAlias = resolveAlias;
  423.     gResolveFolderAlias = resolveFolderAlias;
  424.  
  425.     long startDir;
  426.     if (folderName != nil)
  427.         startDir = GetFolderDirID(folderName, startDirID);
  428.     else
  429.         startDir = startDirID;
  430.     ScanContents(startDir);
  431.     if (gCurFileIndex > 0)
  432.         return gCurFileIndex;
  433.     else
  434.         return 0;
  435. } // END ScanFolderByType
  436.  
  437. // ----------------------------------------------------------------
  438.  
  439. short ScanFolderByCreator(Str63 folderName, long startDirID, OSType restriction, FSSpec *fileArray,
  440.         short arraySize, Boolean resolveAlias, Boolean resolveFolderAlias) {
  441.     InitScan(fileArray, arraySize);
  442.     gCreatorType = restriction;
  443.     gRestrictByType = false;
  444.     gRestrictByCreator = true;
  445.     gRestrictByName = false;
  446.     gResolveAlias = resolveAlias;
  447.     gResolveFolderAlias = resolveFolderAlias;
  448.  
  449.     long startDir;
  450.  
  451.     if (folderName != nil)
  452.         startDir = GetFolderDirID(folderName, startDirID);
  453.     else
  454.         startDir = startDirID;
  455.     ScanContents(startDir);
  456.     if (gCurFileIndex > 0)
  457.         return gCurFileIndex;
  458.     else
  459.         return 0;
  460. } // END ScanFolderByCreator
  461.  
  462. // ----------------------------------------------------------------
  463.  
  464. short ScanFolderSpecific(Str63 folderName, long startDirID,
  465.         OSType fType, OSType fCreator, FSSpec *fileArray, short arraySize,
  466.         Boolean resolveAlias, Boolean resolveFolderAlias) {
  467.     InitScan(fileArray, arraySize);
  468.     gCreatorType = fCreator;
  469.     gFileType = fType;
  470.     gRestrictByType = true;
  471.     gRestrictByCreator = true;
  472.     gRestrictByName = false;
  473.     gResolveAlias = resolveAlias;
  474.     gResolveFolderAlias = resolveFolderAlias;
  475.  
  476.     long startDir;
  477.     if (folderName != nil)
  478.         startDir = GetFolderDirID(folderName, startDirID);
  479.     else
  480.         startDir = startDirID;
  481.     ScanContents(startDir);
  482.     if (gCurFileIndex > 0)
  483.         return gCurFileIndex;
  484.     else
  485.         return 0;
  486. } // END ScanFolderSpecific
  487.  
  488. // ----------------------------------------------------------------
  489.  
  490. short ScanFolderByName(Str63 folderName, long startDirID, Str63 fileName,
  491.                     FSSpec *fileArray, short arraySize, Boolean resolveAlias, Boolean resolveFolderAlias) {
  492.     InitScan(fileArray, arraySize);
  493.     Copy(gFileName, fileName, 1, Length(fileName));
  494.     gRestrictByType = false;
  495.     gRestrictByCreator = false;
  496.     gRestrictByName = true;
  497.     gResolveAlias = resolveAlias;
  498.     gResolveFolderAlias = resolveFolderAlias;
  499.  
  500.     long startDir;
  501.     if (folderName != nil)
  502.         startDir = GetFolderDirID(folderName, startDirID);
  503.     else
  504.         startDir = startDirID;
  505.     ScanContents(startDir);
  506.     if (gCurFileIndex > 0)
  507.         return gCurFileIndex;    // Should be one, since we're scanning by a file's name
  508.     else
  509.         return 0;
  510. } // END ScanFolderByName
  511.  
  512. // ----------------------------------------------------------------
  513.  
  514. void PrefixColon(FSSpec *fileSpec) {
  515.     Precat(fileSpec->name, fileSpec->name, ':');
  516. }
  517.  
  518. void PrefixColons(FSSpec *fileArray, short arraySize) {
  519.     for (short i = 0; i < arraySize; i++)
  520.         PrefixColon(&fileArray[i]);
  521. }